/****************************************************************************

	COPYRIGHT(C) MAXSI SOFTWARE, JONAS 'SORTIE' TERMANSEN 2008, 2009, 2010

    This file is part of Maxsi Engine.

    Maxsi Engine is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Maxsi Engine is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with Maxsi Engine. If not, see <http://www.gnu.org/licenses/>.

	*/ #include "MaxsiEngineContributors.h" /*

	If you modify this file, please enter your name below and provide contact
	information in MaxsiEngineContributors.h. For more information please see
	MaxsiEngineContributors.h.
	
	Contributors to this file:

	- Jonas 'Sortie' Termansen
	- [your name here]

	MaxsiEngine.dll
	A linkable version of Maxsi Engine

	MaxsiWin32.cpp
	Basic Win32 Functionallity

****************************************************************************/


#ifndef MaxsiWin32_H
#define MaxsiWin32_H

#include "windows.h"
#include "TCHAR.H"

namespace MaxsiEngine
{
#ifdef ME_USE_MULTIBYTE_STRING
	#define SendMessageM SendMessageW
	#define CreateWindowM CreateWindowW
	#define GetWindowLongM GetWindowLongW
	#define WSAAddressToStringM WSAAddressToStringW
	#define FormatMessageM FormatMessageW
	#define WNDCLASSM WNDCLASSW
	#define RegisterClassM RegisterClassW
	#define MessageBoxM MessageBoxW
#else
	#define SendMessageM SendMessageA
	#define CreateWindowM CreateWindowA
	#define GetWindowLongM GetWindowLongA
	#define WSAAddressToStringM WSAAddressToStringA
	#define FormatMessageM FormatMessageA
	#define WNDCLASSM WNDCLASSA
	#define RegisterClassM RegisterClassA
	#define MessageBoxM MessageBoxA
#endif

	class MaxsiUI_Form;

	struct	ME_Thread_Parameters
	{
		int		Command;
		int		Parameter;
		int		nCmdShow;
		HWND	Parent;
	};

#ifndef __MINGW_H
	typedef LONG (WINAPI* type_ExceptionFilter  ) (__in struct _EXCEPTION_POINTERS *ExceptionInfo );
	LINK type_ExceptionFilter ME_GetExceptionFilter();
#else
	LINK void* ME_GetExceptionFilter();
#endif

	// Basic Interthread Commands

	#define ME_Thread_Cmd_Null							0x0000
	#define ME_Thread_Cmd_Create						0x0001
	#define ME_Thread_Cmd_GetDefaultStackSpace			0x0002
	#define ME_Thread_Cmd_Register						0x0003
	#define ME_Thread_Cmd_Initialize					0x0004
	#define ME_Thread_Cmd_Terminate						0x0005
	#define ME_Thread_Cmd_TerminateSubThread			0x0006
	#define ME_Thread_Cmd_TerminateSubThreads			0x0007
	#define ME_Thread_Cmd_TerminateButLeaveSubThreads	0x0008
	#define ME_Thread_Cmd_SetParent						0x0009
	#define ME_Thread_Cmd_GetME_WindowPointer			0x000A


	// Advanced Interthread Commands

	#define ME_Thread_Cmd_GetThreadList					0x0100
	
	// Debug

	#define ME_Assertion_Failure						0xF000

	#define ME_TODO(Information) \
	{	ME_DEBUG_BREAKPOINT \
		MESTR* Line	= UIToA((unsigned int)__LINE__); \
		MESTR* Error = BuildString(6, Information, \
			_MESTR( "\n\nThe application tried to run unimplemented code in " __FILE__ " in "), __FUNCTION__, _MESTR(" on line "), Line, _MESTR( " built on " __DATE__ "\n\n"  \
			"Execution can continue, but the behavior of the program is undefined and it is recommended to " \
			"terminate the program and restart. This error shouldn't happen so please contact Maxsi Support " \
			"at http://www.maxsi.dk/support/ so that we can fix the error.\n\n" \
			"Hit Abort to stop execution.\n" \
			"Hit Retry to continue execution and continue recieving errors.\n" \
			"Hit Ignore to continue execution but ignore any further errors.") ); \
		delete[] Error; \
		delete[] Line;	}

#define	DEBUG_RELEASE_BUILD
//#undef	DEBUG_RELEASE_BUILD

#ifdef _DEBUG
	#define	ME_DEBUG_BREAKPOINT __debugbreak();
	#define ME_TODO_DEBUG(Information) ME_TODO(Information)
	#define ME_TODO_RELEASE(Information) __noop
	#define	ME_DONT_COMPILE_RELEASE_BUILD __noop
#elif defined(DEBUG_RELEASE_BUILD)
	#define	ME_DEBUG_BREAKPOINT // __debugbreak();
	#define ME_TODO_DEBUG(Information) ME_TODO(Information)
	#define ME_TODO_RELEASE(Information) __noop
	#define	ME_DONT_COMPILE_RELEASE_BUILD __noop
#else
	#define	ME_DEBUG_BREAKPOINT
	#define ME_TODO_DEBUG(Information) __noop
	#define ME_TODO_RELEASE(Information) ME_TODO(Information)
	#define	ME_DONT_COMPILE_RELEASE_BUILD TODO_Implement_Code_Around_Here

#endif
	
	#define ME_UNDEFINED_BEHAVIOR(Information) \
	{	ME_DEBUG_BREAKPOINT \
		MESTR* Line	= UIToA((unsigned int)__LINE__); \
		MESTR* Error = BuildString(4, Information, \
			_MESTR("\n\nA non-fatal error was detected in " __FILE__ " in " __FILE__ " in "), __FUNCTION__, _MESTR(" on line "), Line, _MESTR( " built on " __DATE__ "\n\n"  \
			"Execution can continue, but the behavior of the program is undefined and it is recommended to " \
			"terminate the program and restart. This error shouldn't happen so please contact Maxsi Support " \
			"at http://www.maxsi.dk/support/ so that we can fix the error.\n\n" \
			"Hit Abort to stop execution.\n" \
			"Hit Retry to continue execution and continue recieving errors.\n" \
			"Hit Ignore to continue execution but ignore any further errors.") ); \
		if ( IssueMEAssertionFailure(Error) == false && ME_GetExceptionFilter() != NULL ) \
		{ \
			throw Information; \
		} \
		delete[] Error; \
		delete[] Line; \
	}
	
	#define ME_UNDEFINED_BEHAVIORMESTRC(Information) \
	{	ME_DEBUG_BREAKPOINT \
		if ( IssueMEAssertionFailure(_MESTR(Information)) == false && ME_GetExceptionFilter() != NULL ) \
		{ \
			throw _MESTR(Information); \
		} \
	}

#ifdef _DEBUG
	#define	ME_UNDEFINED_BEHAVIOR_DEBUG(Information) ME_UNDEFINED_BEHAVIOR(Information)
#elif defined(DEBUG_RELEASE_BUILD)
	#define	ME_UNDEFINED_BEHAVIOR_DEBUG(Information) __noop
#else
	#define	ME_UNDEFINED_BEHAVIOR_DEBUG(Information) __noop
#endif
	

	// Custom Interthread Commands

	// Everything below 0xFFFF is reserved anything above is custom
	#define ME_Thread_Cmd_Custom						0xFFFF

	// Flags for ME_Thread_Cmd_Terminate
	#define	ME_Thread_Cmd_FullTermination				0x0000
	#define	ME_Thread_Cmd_SelfTerminate					0x0001
	#define	ME_Thread_Cmd_TerminateChildren				0x0002


	// Declares the interface to call into a thread
	#define	ME_THREAD(Name,Class) THREADED_VARIABLE Class* d_##Class; DWORD WINAPI Name(LPVOID parameter) { d_##Class = new Class; DWORD Result = d_##Class->EntryPoint(parameter); delete d_##Class; delete[] (char*)parameter; return Result; } \
	LRESULT APIENTRY	Name##Proc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { return d_##Class->WindowProc(hWnd,message,wParam,lParam); }
	
	#define	ME_THREAD_H(Name,Class) \
	DWORD WINAPI Name (LPVOID parameter);  \
	LRESULT APIENTRY	Name##Proc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

	#define	ME_THREAD_LINK(Name,Class,Linking)  THREADED_VARIABLE Class* d_##Class; Linking DWORD WINAPI Name(LPVOID parameter) { d_##Class = new Class; DWORD Result = d_##Class->EntryPoint(parameter); delete d_##Class; delete[] (char*)parameter; return Result; } \
	Linking LRESULT APIENTRY	Name##Proc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { return d_##Class->WindowProc(hWnd,message,wParam,lParam); }
	
	#define	ME_THREAD_LINK_H(Name,Class,Linking) \
	Linking DWORD WINAPI Name (LPVOID parameter);  \
	Linking LRESULT APIENTRY	Name##Proc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);


	// Declares the interface to call into a thread
	#define	ME_WIN32ENTRYPOINT(ApplicationClass) ApplicationClass Application; \
	int WINAPI			WinMain					(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) \
	{ \
		SetWin32EntryPointClass(dynamic_cast<ME_Win32Entry*>(&Application)); \
		return Application.WinMain(hInstance,hPrevInstance,lpCmdLine,nCmdShow); \
	} \
	LRESULT APIENTRY ApplicationClass##Proc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) \
	{ \
		return	Application.WindowProc(hWnd,message,wParam,lParam); \
	}

	#define	ME_WIN32ENTRYPOINT_H(ApplicationClass) \
	int WINAPI			WinMain					(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow); \
	LRESULT APIENTRY ApplicationClass##Proc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

	CLASSLINK	ME_Window
	{
	public:
		ME_Window();
		~ME_Window();

		virtual		BOOL				InitApplication		(HANDLE hInstance);
		virtual		BOOL				InitInstance		(HINSTANCE hInstance, INT nCmdShow);
		virtual		LRESULT APIENTRY	WindowProc			(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
		virtual		BOOL				TerminateApplication(void);

	public:
		HWND			WindowHWND;
		MaxsiUI_Form*	Form;
		List			SubWindows;

	};

	CLASSLINK	ME_Thread : public ME_Window
	{
		Declare_Class(ME_Thread,ME_Window);

	public:
		ME_Thread();
		~ME_Thread();

		virtual		DWORD WINAPI		EntryPoint			(LPVOID parameter);
		virtual		DWORD				DefaultStackSpace	(void);
		virtual		LRESULT APIENTRY	WindowProc			(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
		virtual		LRESULT				ME_Thread_Cmd		(HWND hWnd, WPARAM wParam, LPARAM lParam);

		// Commands

		virtual		DWORD				OnThreadRegistered	(HWND hWnd);
		virtual		DWORD				OnThreadInitialize	(LPARAM lParam);
		virtual		DWORD				OnThreadTerminate	(LPARAM lParam);
		virtual		DWORD				OnNewParent			(HWND hWnd);
		virtual		DWORD				OnMEAssertionFailure(char* Message);

	public:
		HACCEL		hAccTable;
		HINSTANCE	hInst;
		HWND		Parent;
		List		SubThreads;

	};

	CLASSLINK	ME_Win32Entry : public ME_Thread
	{
		Declare_Class(ME_Win32Entry,ME_Thread);

	public:
		virtual		int WINAPI			WinMain				(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow);
					BOOL				InitApplication		(HANDLE hInstance);
					BOOL				InitInstance		(HINSTANCE hInstance, INT nCmdShow);
		virtual		LRESULT APIENTRY	WindowProc			(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

	};

	
	// Functions
	LINK			HANDLE				ME_CreateThread			(LPTHREAD_START_ROUTINE lpStartAddress, HWND Parent = 0);
	LINK			BOOL				ME_PostThreadMessage	(HWND hWnd, WPARAM wParam, LPARAM lParam);
	LINK			LRESULT				ME_SendThreadMessage	(HWND hWnd, WPARAM wParam, LPARAM lParam);
	LINK			ME_Window*			ME_WindowGetPointer		(HWND Handle);
	LINK	const	UINT				ME_ThreadMessageIDFunc	(void);
	#define	ME_ThreadMessageID			ME_ThreadMessageIDFunc	()
	LINK			bool				SetWin32EntryPointClass	(ME_Win32Entry* NewEntry);
	LINK			bool				IssueMEAssertionFailure	(MESTR* Message);

}

#endif